package my.db;

import java.io.Serializable;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import my.cache.CacheManager;

import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.commons.dbcp.BasicDataSource;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.ColumnListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;
import org.apache.commons.lang.ArrayUtils;

public class DbManager {
	private static BasicDataSource dataSource;
	public static QueryRunner db;

	static {
		try {
			Configuration config = new PropertiesConfiguration("app.properties");
			dataSource = new BasicDataSource();
			dataSource.setDriverClassName(config.getString("db.driver"));
			dataSource.setUsername(config.getString("db.username"));
			dataSource.setPassword(config.getString("db.password"));
			dataSource.setUrl(config.getString("db.url"));

			db = new QueryRunner(dataSource);
		} catch (ConfigurationException e) {
			throw new DbException(e);
		}
	}

	private static final List<Class<?>> PRIMITIVE_CLASSES = new ArrayList<Class<?>>() {
		private static final long serialVersionUID = -1954157585138995043L;
		{
			add(Long.class);
			add(Integer.class);
			add(String.class);
			add(java.util.Date.class);
			add(java.sql.Date.class);
			add(java.sql.Timestamp.class);
		}
	};

	private static final ScalarHandler SCALAR_HANDLER = new ScalarHandler();

	private static final ColumnListHandler COLUMN_LIST_HANDLER = new ColumnListHandler();

	private static final boolean isPrimitive(Class<?> clazz) {
		return clazz.isPrimitive() || PRIMITIVE_CLASSES.contains(clazz);
	}

	@SuppressWarnings("unchecked")
	public static <T> T get(Class<T> clazz, String sql, Object... params) {
		try {
			return (T) db.query(sql, isPrimitive(clazz) ? SCALAR_HANDLER
					: new BeanHandler<T>(clazz), params);
		} catch (SQLException e) {
			throw new DbException(e);
		}
	}

	@SuppressWarnings("unchecked")
	public static <T> List<T> list(Class<T> clazz, String sql, Object... params) {
		try {
			return (List<T>) db.query(sql,
					isPrimitive(clazz) ? COLUMN_LIST_HANDLER
							: new BeanListHandler<T>(clazz), params);
		} catch (SQLException e) {
			throw new DbException(e);
		}
	}

	public static <T> List<T> paging(Class<T> clazz, String sql, int page,
			int pageSize, Object... params) {

		if (page <= 0 || pageSize < 0) {
			throw new IllegalArgumentException(
					"Illegal parameter of 'page' or 'pageSize', Must be positive.");
		}
		int from = (page - 1) * pageSize;
		int count = pageSize > 0 ? pageSize : Integer.MAX_VALUE;

		return list(clazz, sql + " LIMIT ?,?",
				ArrayUtils.addAll(params, new Integer[] { from, count }));

	}

	@SuppressWarnings("unchecked")
	public static <T> T cachedGet(String cacheRegion, Serializable key,
			Class<T> clazz, String sql, Object... params) {
		T obj = (T) CacheManager.get(cacheRegion, key);
		if (obj == null) {
			obj = (T) get(clazz, sql, params);
			CacheManager.set(cacheRegion, key, (Serializable) obj);
		}
		return obj;
	}

	@SuppressWarnings("unchecked")
	public static <T> List<T> cachedList(String cacheRegion, Serializable key,
			Class<T> clazz, String sql, Object... params) {
		List<T> obj = (List<T>) CacheManager.get(cacheRegion, key);
		if (obj == null) {
			obj = (List<T>) list(clazz, sql, params);
			CacheManager.set(cacheRegion, key, (Serializable) obj);
		}
		return obj;
	}

	@SuppressWarnings("unchecked")
	public static <T> List<T> cachedPaging(String cacheRegion,
			Serializable key, Class<T> clazz, String sql, int page,
			int pageSize, Object... params) {
		List<T> obj = (List<T>) CacheManager.get(cacheRegion, key);
		if (obj == null) {
			obj = (List<T>) paging(clazz, sql, page, pageSize, params);
			CacheManager.set(cacheRegion, key, (Serializable) obj);
		}
		return obj;
	}

	public static int update(String sql, Object... params) {
		try {
			return db.update(sql, params);
		} catch (SQLException e) {
			throw new DbException(e);
		}
	}

	public static int[] batch(String sql, Object[][] params) {
		try {
			return db.batch(sql, params);
		} catch (SQLException e) {
			throw new DbException(e);
		}
	}

	public static void destroy() {
		if (dataSource != null) {
			try {
				dataSource.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}

	public static void main(String[] args) throws ConfigurationException,
			SQLException {

		long start = System.currentTimeMillis();

		for (String x : DbManager.list(String.class, "select pwd from users")) {
			System.out.println(x);
		}

		long end = System.currentTimeMillis();

		System.out.println((end - start));

		destroy();
	}
}
